Jx.declare('Jx.lib.Hash', {

	$actions: [

		'apply',
		
		'applyIf'

	],

	accessList: null,

	length: 0,

	constructor: function(data){

		this.prepareCache(this.accessList = {}, this.$cache);

		this.apply(data);

		this.$super();

	},
	
	_onApplyHash: function(data, ifNotSet){
		
		for(var validatorName, name, value, c = 0, l = data.length; c<l; c++){
				
			value = data.get( name = data[c] );
			
			validatorName = 'allowSet'+name.charAt(0).toUpperCase()+name.substring(1, name.length);

			if ( (!ifNotSet || !this.isSet(name)) &&
				
				(!(validatorName in this) || this[validatorName]({}, value) !== false)
				
			){
				
				
				this.set(name, data.get(name));
				
			}
			
		}
		
	},
	
	_onApplyObject: function(data, ifNotSet){
		
		Jx.each(data, function(n, v, o){
			
			var me = this.me,
			
				ifNotSet = this.ifNotSet;
	
			if (o.hasOwnProperty(n) && typeof v != 'undefined'){

				var validatorName = 'allowSet'+n.charAt(0).toUpperCase()+n.substring(1, n.length);

				if ( (!ifNotSet || !me.isSet(n)) &&
					
					(!(validatorName in me) || me[validatorName]({}, v) !== false)
					
				){

					me.set(n, v);

				}

			}

		}, { me: this, ifNotSet: ifNotSet });
		
	},

	onApply: function(operation, data){
		
		if ( Jx.instanceOf(data, 'Jx.lib.Hash') ){
			
			this._onApplyHash(data, false);
			
		} else {
			
			this._onApplyObject(data, false);
		
		}

	},
	
	onApplyIf: function(operation, data){
		
		if ( Jx.instanceOf(data, 'Jx.lib.Hash') ){
			
			this._onApplyHash(data, true);
			
		} else {
			
			this._onApplyObject(data, true);
		
		}

	},

	onSet: function(name, value){

		var data = this.$cache;

		if (!data.hasOwnProperty(name)){

			Array.prototype.push.call(this, name);

		}

		this.$super(arguments);

	},

	allowApply: function(operation, data){

		return Jx.isObject(data);

	},
	
	allowApplyIf: function(operation, data){

		return Jx.isObject(data);

	},

	isValid: function(name, value){

		return typeof value != 'undefined' &&

			this.$super(arguments) &&

			(

				!this.strictAccess ||

				(Jx.isString(name) && this.accessList.hasOwnProperty(name))

			);

	},

	get: function(name){

		var nil;

		return this.isSet(name) ?

			this.$super(arguments)

			:

			nil;

	},

	unset: function(name){

		if (this.isSet(name)){

			Array.prototype.splice.call(this,

				Array.prototype.indexOf.call(this, name),

				1

			);

			delete this.$cache[name];

		}

		return this;

	},
	
	toObject: function(){
		
		var target = {},
		
			data = this.$cache;
		
		for(var name, c=0, l = this.length; c < l; c++){
			
			name = this[c];
			
			target[ name ] = this.get(name);
			
		}
		
		return target;
		
	},

	prepareCache: function(accessList, cache){

		Jx.each(cache, function(n, v, o){

			this[n] = true;

		}, accessList);

	}

});
